#pragma once

#include "string.hpp"

namespace zuck
{
    template<typename StringType>
    class _StringViewConstIterator {
    private:
        using _Self = _StringViewConstIterator<StringType>;
    public:
        using value_type        = typename StringType::value_type;
        using pointer           = typename StringType::const_pointer;
        using reference         = typename StringType::const_reference;
        using difference_type   = typename StringType::difference_type;
        using iterator_category = zuck::random_access_iterator_tag;
    private:
        pointer _m_iter;
        pointer _m_first;
        pointer _m_last;
    public:
        constexpr explicit _StringViewConstIterator(pointer iter, pointer first, pointer last)noexcept
            : _m_iter(iter), _m_first(first), _m_last(last) {}

        constexpr _StringViewConstIterator(_Self const& other)
            : _m_iter(other._m_iter), _m_first(other._m_first), _m_last(other._m_last) {}

        constexpr _StringViewConstIterator(_Self&& other)noexcept
            : _m_iter(other._m_iter), _m_first(other._m_first), _m_last(other._m_last)
        {
            other._m_iter  = null;
            other._m_first = null;
            other._m_last  = null;
        }

        ~_StringViewConstIterator()
        {
            _m_iter  = null;
            _m_first = null;
            _m_last  = null;
        }

        [[nodiscard]] constexpr value_type* get_unwrapped()noexcept
        {
            return _m_iter;
        }

        constexpr _Self& operator=(_Self const& other)
        {
            _m_iter  = other._m_iter;
            _m_first = other._m_first;
            _m_last  = other._m_last;
            return *this;
        }

        constexpr _Self& operator=(_Self&& other)noexcept
        {
            _m_iter  = other._m_iter;
            _m_first = other._m_first;
            _m_last  = other._m_last;
            other._m_iter  = null;
            other._m_first = null;
            other._m_last  = null;
            return *this;
        }

        [[nodiscard]] constexpr pointer operator->()const noexcept
        {
            if constexpr (debug_) {
                if (_m_iter >= _m_last || _m_iter < _m_first) {
                    debug_log::derror(zuck::SEG_WrongIter, 
                        "Segmentfault: invalid iterator");
                }
            }
            return _m_iter;
        }

        [[nodiscard]] constexpr reference operator*()const noexcept
        {
            if constexpr (debug_) {
                if (_m_iter >= _m_last || _m_iter < _m_first) {
                    debug_log::derror(zuck::SEG_WrongIter,
                        "Segmentfault: invalid iterator");
                }
            }
            return *_m_iter;
        }

        constexpr _Self& operator++()noexcept
        {
            if constexpr (debug_) {
                if (_m_iter + 1 >= _m_last) {
                    debug_log::derror(zuck::SEG_OutofRange, 
                        "Segmentfault: iter out of range !");
                }
            }
            ++_m_iter;
            return *this;
        }

        [[nodiscard]] constexpr _Self operator++(int)noexcept
        {
            auto _Tmp = *this;
            ++(*this);
            return _Tmp;
        }

        constexpr _Self& operator+=(const difference_type _Offset)noexcept
        {
            if constexpr (debug_) {
                _verify_offset(_Offset);
            }
            _m_iter += _Offset;
            return *this;
        }

        [[nodiscard]] constexpr _Self operator+(difference_type const _Offset)const noexcept
        {
            auto _Tmp = *this;
            return _Tmp += _Offset;
        }

        [[nodiscard]] constexpr reference operator[](difference_type const _Offset)const noexcept
        { return *(*this + _Offset); }

        constexpr _Self& operator--()noexcept
        {
            if constexpr (debug_) {
                if (_m_iter - 1 < _m_first) {
                    debug_log::derror(zuck::SEG_OutofRange, 
                        "Segmentfault: iter out of range !");
                }
            }
            --_m_iter;
            return *this;
        }

        [[nodiscard]] constexpr _Self operator--(int)noexcept
        {
            auto _Tmp = *this;
            --(*this);
            return _Tmp;
        }

        constexpr _Self& operator-=(difference_type const _Offset)noexcept
        {
            if constexpr (debug_) {
                _verify_offset(_Offset);
            }
            _m_iter -= _Offset;
            return *this;
        }

        [[nodiscard]] constexpr _Self operator-(difference_type const _Offset)const noexcept
        {
            auto _Tmp = *this;
            return _Tmp -= _Offset;
        }

        [[nodiscard]] constexpr difference_type operator-(const _Self& other)const noexcept
        {
            return static_cast<difference_type>(_m_iter - other._m_iter);
        }

        [[nodiscard]] constexpr difference_type operator-(pointer other)const noexcept
        {
            return static_cast<difference_type>(_m_iter - other);
        }

        [[nodiscard]] constexpr bool operator==(_Self const& other)const noexcept
        {
            return static_cast<bool>(_m_iter == other._m_iter);
        }

        [[nodiscard]] constexpr bool operator!=(_Self const& other)const noexcept
        {
            return static_cast<bool>(_m_iter != other._m_iter);
        }

        [[nodiscard]] constexpr bool operator<(_Self const& other)const noexcept
        {
            return static_cast<bool>(_m_iter < other._m_iter);
        }

        [[nodiscard]] constexpr bool operator<=(_Self const& other)const noexcept
        {
            return static_cast<bool>(_m_iter <= other._m_iter);
        }

        [[nodiscard]] constexpr bool operator>(_Self const& other)const noexcept
        {
            return static_cast<bool>(_m_iter > other._m_iter);
        }

        [[nodiscard]] constexpr bool operator>=(_Self const& other)const noexcept
        {
            return static_cast<bool>(_m_iter >= other._m_iter);
        }

    private:
        constexpr void _verify_offset(const difference_type offset)
        {
            if (_m_iter + offset >= _m_last || _m_iter + offset < _m_first) {
                debug_log::derror(zuck::SEG_OutofRange, 
                    "Segmentfault: invalid offset !");
            }
        }
    };


    template<typename CharType, typename Traits = zuck::_CharTraits<CharType>>
    class StringView {
    private:
        using _Self = StringView<CharType, Traits>;
        using _Alty = zuck::allocator_traits<zuck::allocator<CharType>>;
    public:
        using value_type      = CharType;
        using pointer         = CharType*;
        using const_poniter   = const CharType*;
        using size_type       = zuck::size_t;
        using reference       = CharType&;
        using const_reference = const CharType&;
        using difference_type = zuck::ptrdiff_t;
    public:
        using iterator               = _StringViewConstIterator<StringView<CharType>>;
        using const_iterator         = _StringViewConstIterator<StringView<CharType>>;
        using reverse_iterator       = zuck::reverse_iterator<iterator>;
        using const_reverse_iterator = zuck::reverse_iterator<const_iterator>;
    private:
        const_poniter _m_start;
        const_poniter _m_last;
    public:
        static auto constexpr npos = static_cast<size_type>(-1);
    public:
        constexpr explicit StringView()noexcept
            : _m_start(), _m_last() {}

        constexpr StringView(const CharType* const data)
            : _m_start(), _m_last()
        {
            __construct(data);
        }

        constexpr StringView(const CharType* const _Data, size_type const _Newsize)
            : _m_start(_Data), _m_last(_Data + _Newsize) {}

        constexpr StringView(std::initializer_list<CharType> data)
            : _m_start(data.begin()), _m_last(data.end()) {}

        template<typename InputIterator>
        constexpr explicit StringView(InputIterator first, InputIterator last)
            : _m_start(&(*first)), _m_last(&(*last)) {}

        constexpr StringView(const StringView& other)
            : _m_start(other._m_start), _m_last(other._m_last) {}

        constexpr StringView(StringView&& other)noexcept
            : _m_start(other._m_start), _m_last(other._m_last)
        {
            other._m_start = null;
            other._m_last  = null;
        }

        [[nodiscard]] constexpr const_reference at(const size_type _Idx)const noexcept
        {
            auto const _Size = size();
            if (out_of_range(_Idx, _Size)) {
                debug_log::derror(zuck::SEG_OutofRange, 
                    "Segmentfault: subscript out of range !");
            }
            return *(_m_start + _Idx);
        }

        [[nodiscard]] constexpr int compare(_Self const& other)const noexcept
        {
            return __compare_str(other.c_str());
        }

        [[nodiscard]] constexpr int compare(const CharType* const _Elem)const noexcept
        {
            return __compare_str(_Elem);
        }

        [[nodiscard]] constexpr const_poniter c_str()const noexcept
        {
            return _m_start;
        }

        [[nodiscard]] constexpr const_poniter data()const noexcept
        {
            return _m_start;
        }

        [[nodiscard]] constexpr const_reference front()const noexcept
        {
            return *_m_start;
        }

        [[nodiscard]] constexpr const_reference back()const noexcept
        {
            return *(_m_last - 1);
        }

        [[nodiscard]] constexpr StringView substr(size_type const start, size_type const length = npos)
        {
            if (!(length ^ npos)) {
                return _Self{};
            }

            auto const _Oldsize = size();
            if (start >= _Oldsize) {
                _Self{};
            }
            
            return _Self(_m_start + start, _m_start + length);
        }

        [[nodiscard]] constexpr StringView substr(const_iterator start, const_iterator end)
        {
            return _Self(start, end);
        }

        constexpr void swap(StringView& other)noexcept
        {
            if (*this != other) {
                auto _Tmp = zuck::move(*this);
                *this     = zuck::move(other);
                other     = zuck::move(_Tmp);
            }
        }
        
        [[nodiscard]] constexpr size_type size()const noexcept
        {
            return static_cast<size_type>(_m_last - _m_start);
        }

        [[nodiscard]] constexpr size_type max_size()const noexcept
        {
            return _Alty::max_size();
        }

        [[nodiscard]] constexpr bool empty()const noexcept
        {
            return static_cast<bool>(_m_last == _m_start);
        }

        [[nodiscard]] constexpr iterator begin()const noexcept
        {
            return iterator(_m_start, _m_start, _m_last);
        }

        [[nodiscard]] constexpr iterator end()const noexcept
        {
            return iterator(_m_last, _m_start, _m_last);
        }

        [[nodiscard]] constexpr const_iterator cbegin()const noexcept
        {
            return const_iterator(_m_start, _m_start, _m_last);
        }

        [[nodiscard]] constexpr const_iterator cend()const noexcept
        {
            return const_iterator(_m_last, _m_start, _m_last);
        }

        [[nodiscard]] constexpr reverse_iterator rbegin()const noexcept
        {
            return reverse_iterator(_m_last - 1, _m_start, _m_last);
        }

        [[nodiscard]] constexpr reverse_iterator rend()const noexcept
        {
            return reverse_iterator(_m_start - 1, _m_start, _m_last);
        }

        [[nodiscard]] constexpr const_reverse_iterator crbegin()const noexcept
        {
            return const_reverse_iterator(_m_last - 1, _m_start, _m_last);
        }

        [[nodiscard]] constexpr const_reverse_iterator crend()const noexcept
        {
            return const_reverse_iterator(_m_start - 1, _m_start, _m_last);
        }

        [[nodiscard]] constexpr size_type find(CharType const& ch, size_type const off)
        {
            auto* _Tmp = _m_start + off;
            __verify_ptr(_Tmp);
            for (; _Tmp != _m_last; ++_Tmp) {
                if (*_Tmp == ch) {
                    return (_Tmp - _m_start);
                }
            }
            return npos;
        }

        [[nodiscard]] constexpr size_type find_first_of
        (CharType const ch, size_type _Idx = 0)const noexcept 
        {
            for (; _Idx < size(); ++_Idx) {
                if (_m_start[_Idx] == ch) {
                    return _Idx;
                }
            }
            return npos;
        }

        [[nodiscard]] constexpr size_type find_first_not_of
        (CharType const ch, size_type _Idx = 0)const noexcept
        {
            for (; _Idx < size(); ++_Idx) {
                if (_m_start[_Idx] != ch) {
                    return _Idx;
                }
            }
            return npos;
        }

        [[nodiscard]] constexpr size_type find_first_of
        (CharType const* data, size_type const _Idx = 0)const noexcept
        {
            return __find_first_str(data, _Idx);
        }

        [[nodiscard]] constexpr size_type find_first_not_of
        (CharType const* data, size_type const _Idx = 0)const noexcept
        {
            auto _Res = __find_first_str(data, _Idx);
            if (_Res == npos) {
                return size_type{};
            }
            if (_Res) {
                return size_type{};
            }
            return _Res + __length(data);
        }

        [[nodiscard]] constexpr size_type find_last_of
        (CharType const ch, size_type _Idx = npos)const noexcept
        {
            _Idx = (_Idx == npos) ? (size() - 1) : _Idx;
            for (;_Idx; --_Idx) {
                if (_m_start[_Idx] == ch) {
                    return _Idx;
                }
            }
            return npos;
        }

        [[nodiscard]] constexpr size_type find_last_of
        (CharType const* data, size_type const _Idx = npos)const noexcept
        {
            return __find_last_str(data, _Idx);
        }

        [[nodiscard]] constexpr size_type find_last_not_of
        (CharType const ch, size_type _Idx = npos)const noexcept
        {
            _Idx = (_Idx == npos) ? (size() - 1) : _Idx;
            for (; _Idx; --_Idx) {
                if (_m_start[_Idx] != ch) {
                    return _Idx;
                }
            }
            return npos;
        }

        [[nodiscard]] constexpr size_type find_last_not_of
        (CharType const* data, size_type const _Idx = npos)const noexcept
        {
            _Idx = (_Idx == npos) ? (size() - 1) : _Idx;
            
            return npos;
        }

        constexpr StringView& operator=(StringView const& other)
        {
            _m_start = other._m_start;
            _m_last  = other._m_last;
            return *this;
        }

        constexpr StringView& operator=(StringView&& other)
        {
            _m_start = other._m_start;
            _m_last  = other._m_last;
            other._m_start = null;
            other._m_last  = null;
            return *this;
        }

        [[nodiscard]] constexpr const_reference operator[](size_type const _Idx)const noexcept
        {
            if (_Idx >= size()) {
                debug_log::derror(zuck::SEG_OutofRange, "subscript out of range");
            }
            return *(_m_start + _Idx);
        }

        [[nodiscard]] constexpr bool operator==(StringView const& other)const noexcept
        {
            return static_cast<bool>(__compare_str(other.data()) == 0);
        }

        [[nodiscard]] constexpr bool operator!=(StringView const& other)const noexcept
        {
            return static_cast<bool>(__compare_str(other.data()) != 0);
        }

        [[nodiscard]] constexpr bool operator<(StringView const& other)const noexcept
        {
            return static_cast<bool>(__compare_str(other.data()) < 0);
        }

        [[nodiscard]] constexpr bool operator>(StringView const& other)const noexcept
        {
            return static_cast<bool>(__compare_str(other.data()) > 0);
        }

        [[nodiscard]] constexpr bool operator<=(StringView const& other)const noexcept
        {
            return static_cast<bool>(__compare_str(other.data()) <= 0);
        }

        [[nodiscard]] constexpr bool operator>=(StringView const& other)const noexcept
        {
            return static_cast<bool>(__compare_str(other.data()) >= 0);
        }

    private:
        [[nodiscard]] constexpr int __compare_str(CharType const* _Elem)const noexcept
        {
            auto* _Proxy = _m_start;
            for (; *_Proxy && (*_Proxy == *_Elem);
                ++_Proxy, ++_Elem);

            int ans = *_Proxy - *_Elem;
            if (ans < 0) {
                return -1;
            } 
            else if (ans > 0) {
                return 1;
            }
            return 0;
        }

        constexpr void __construct(const CharType* const data)
        {
            auto const _Len = __length(data);
            __verify_size(_Len);

            _m_start = data;
            _m_last  = data + _Len;
        }

        template<typename Iterator>
        constexpr void __construct_range(Iterator first, Iterator last)
        {
            auto const _Len = zuck::distance(first, last);
            __verify_size(_Len);
            _m_start = first;
            _m_last  = last;
        }

        [[nodiscard]] constexpr size_type
        __find_first_str(CharType const* const des, size_type off)const noexcept
        {
            int code[256] = { 0 }, n = 0;
            for (int i = 0; des[i]; ++i, ++n) {
                code[des[i]] |= (1 << i);
            }
            int p = 0;
            auto const _Size = size();
            for (; off < _Size; ++off)
            {
                p = (p << 1 | 1) & code[_m_start[off]];
                if (p & (1 << (n - 1))) {
                    return off - n + 1;
                }
            }
            return static_cast<zuck::size_t>(-1);
        }

        [[nodiscard]] constexpr size_type
        __find_last_str(CharType const* const des, size_type const _Deslen, size_type off)const noexcept
        {
            auto const _Size = size();
            size_type _Res = npos;
            off = (off == npos) ? (_Size - 1) : off;
            for (; off > 0 && off < _Size; off -= _Deslen) {
                if ((_Res = __find_first_str(des, off)) != npos) {
                    return _Res;
                }
            }
            return _Res;
        }

        [[nodiscard]] constexpr size_type
        __find_last_not_of_aux(CharType const* const des, size_type off)const noexcept
        {
            auto const len = __length(des);
            auto _Res = __find_last_str(des, off);
            return (_Res != npos) ? (_Res + len) : npos;
        }

        template<typename Iter>
        constexpr void __verify_range(Iter first, Iter last)noexcept
        {
            if (first >= last) {
                debug_log::derror(SegmentCase::SEG_OutofRange, "iterator range is invalid !");
            }
        }

        constexpr void __verify_ptr(const_poniter ptr)
        {
            if (ptr >= _m_last) {
                debug_log::derror(zuck::SEG_WrongIter, "invalid address");
            }
        }

        constexpr void __verify_idx(size_type const _Idx, size_type const _Size)
        {
            if (_Idx >= _Size) {
                debug_log::derror(zuck::SEG_OutofRange, "Segmentfault: subscript out of range !");
            }
        }

        constexpr void __verify_size(size_type _NewCapacity)noexcept
        {
            auto const _Max = max_size();
            if (_NewCapacity > _Max) {
                debug_log::derror(zuck::SEG_LengthTooLong, "String length too long !");
            }
        }

        [[nodiscard]] constexpr size_type __length(CharType const* elem)const noexcept
        {
            size_type _Cnt = 0;
            for (; *elem != CharType{}; ++elem, ++_Cnt);
            return _Cnt;
        }

        constexpr void __check_offset(size_type const _Offset)
        {
            if (_m_start + _Offset > _m_last) {
                debug_log::derror(zuck::SEG_OutofRange, "Segmentfault: offset out of range !");
            }
        }
    };

    template<typename CharType, typename _Traits>
    std::basic_ostream<CharType, _Traits>&
        operator<<(std::basic_ostream<CharType, _Traits>& _Out, const zuck::StringView<CharType, _Traits> _Str)
    {
        return _Output_string(_Out, _Str.data(), _Str.size());
    }

    using string_view    = StringView<char, zuck::char_traits>;
    using wstring_view   = StringView<wchar_t, zuck::wchar_traits>;
    using u16string_view = StringView<char16_t, zuck::_CharTraits<char16_t>>;
    using u32string_view = StringView<char32_t, zuck::_CharTraits<char32_t>>;
#if _HAS_CXX20
    using string8 = String<char8_t, zuck::_CharTraits<char8_t>>;
#endif
}

